use anyhow::anyhow;
use anyhow::ensure;
use anyhow::Result;
use chrono::NaiveTime;
use chrono::Utc;
use fudata::db;
use fudata::model::kline;
use fudata::model::kline::Klt;
use fudata::model::security;
use fudata::model::FudaRecord;
use log::error;
use log::info;
use log::trace;
use reqwest::Client;
use serde_json::Value;
use std::str::FromStr;

/// 更新开放基金历史净值 update opf fund history values
///
/// security: &Security, a security instance
///
/// page_index: u32, e.g. 1, page index to retrieve
///
/// page_size: u32, e.g. 10 , page size to retrieve
///
pub async fn update_opf_history_values_from_eastmoney(
    worker_no: u8,
    security: &security::Security,
    page_index: u32,
    page_size: u32
) -> Result<Vec<FudaRecord>> {
    trace!("Worker[{worker_no}] update_opf_history_values_from_eastmoney--1");
    // If you prefer to return a Result instead of panicking, replace the assert! macro with a similar check using ensure! or an early return with an error:
    ensure!(
        &security.type_ == &security::SecurityType::OPF,
        format!(
            "Worker[{worker_no}] (update_opf_history_values_from_eastmoney) invalid type_, only OPF security: {:?}",
            security
        )
    );
    trace!(
        "Worker[{worker_no}] update_opf_history_values_from_eastmoney--1.1 security.id={:?}",
        security.id
    );
    let security_id = security.id.clone().unwrap().id.to_raw();
    trace!("Worker[{worker_no}] update_opf_history_values_from_eastmoney--1.2");

    let mut updates = Vec::new();
    let jjjz_rs = eastmoney::fund::fund_jjjz::FundJjjz
        ::fund_jjjz(worker_no, &security.symbol, page_index, page_size).await
        .map_err(|err| anyhow!(err.to_string()));

    match jjjz_rs {
        Ok(jjjz) => {
            trace!("Worker[{worker_no}] update_opf_history_values_from_eastmoney--2.1");

            let default_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap_or_default();

            for lsjz in jjjz.data.lsjz_list {
                match
                    (
                        lsjz.fsrq,
                        lsjz.dwjz, //lsjz.ljjz
                    )
                {
                    (
                        Some(Value::String(fsrq)),
                        Some(Value::String(dwjz)),
                        // Some(Value::String(ljjz)),
                    ) => {
                        let parsed_fsrq = dateparser
                            ::parse_with(&fsrq, &Utc, default_time)
                            .map_err(|err| anyhow!(err.to_string()))?;

                        let parsed_dwjz = f64
                            ::from_str(&dwjz)
                            .map_err(|err| anyhow!(err.to_string()))?;
                        // let parsed_ljjz =
                        //     f64::from_str(&ljjz).map_err(|err| anyhow!(err.to_string()))?;

                        let kline = kline::Kline {
                            klt: Klt::DAY,
                            klts: parsed_fsrq,
                            open: parsed_dwjz,
                            high: parsed_dwjz,
                            low: parsed_dwjz,
                            close: parsed_dwjz,
                            ..Default::default()
                        };

                        let updated = db::kline::upsert(&security_id, &kline).await?;

                        if let Some(u) = updated {
                            updates.push(u);
                        }
                    }
                    _ => {
                        // Handle the case where either diff.f12 or diff.f14 is None
                        // or if the unwrapping fails
                        error!("Error: Missing fsrq, dwjz, ljjz");
                    }
                }
            }
            trace!(
                "Worker[{worker_no}] update_opf_history_values_from_eastmoney--3 updates.len={}",
                updates.len()
            );

            Ok(updates)
        }
        Err(e) => {
            trace!("Worker[{worker_no}] update_opf_history_values_from_eastmoney--2.2");
            Err(e)
        }
    }
}

#[cfg(test)]
mod test {
    use crate::fund::opf_value_craw;
    use crate::fund::opfs_craw;

    use fudata::db;
    use fudata::model::security;
    use serde::Deserialize;

    #[derive(Debug, Deserialize, PartialEq)]
    struct Count {
        count: u64,
    }

    #[tokio::test]
    async fn test_update_opf_history_values_from_eastmoney() {
        let _ = db::init().await.unwrap();

        let yesterday = "2024-03-18";
        let rs = opfs_craw::update_opf_list_page_from_eastmoney(yesterday, 1, 3).await;
        println!("(update_opf_list_from_eastmoney) rs={:?}", rs);

        let securities = db::security
            ::select(&None, Some(security::SecurityType::OPF), None, None, 0, 3).await
            .unwrap();

        let ss = db::security::find("SH.OPF.501009").await.unwrap();
        let securities = vec![ss];

        let pz = 100;
        for security in &securities {
            let mut pn = 1;
            print!("\n{security:?}:pz={pz}:pn=");
            loop {
                let r1 = opf_value_craw::update_opf_history_values_from_eastmoney(
                    1,
                    security,
                    pn,
                    pz
                ).await;
                assert_eq!(r1.is_ok(), true);
                if let Ok(vv) = r1 {
                    if vv.len() == 0 {
                        break;
                    } else {
                        print!("{pn}.. vv.len={}", vv.len());
                        pn = pn + 1;
                    }
                }
            }
            println!("\n");
        }
    }
}
